/**
 *    Copyright (C) 2009 Victor Smirnov <aist11@gmail.com>
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License along
 *    with this program; if not, write to the Free Software Foundation, Inc.,
 *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *    See LICENSE.exceptions in the root folder of this project for possible
 *    exceptions
 *
 */

#ifndef _CUBEFS_CORE_API_METADATA_CONTAINER_HPP
#define _CUBEFS_CORE_API_METADATA_CONTAINER_HPP

#include <cubefs/api/metadata/container.hpp>
#include <cubefs/core/api/metadata/group.hpp>

#include <cubefs/core/api/metadata/page.hpp>
#include <cubefs/core/api/metadata/model.hpp>

#define CUBEFS_SOURCE "cubefs/core/api/metadata/container.hpp:" CUBEFS_AT

namespace cubefs { namespace api {

template <typename Interface>
class ContainerMetadataImplT: public MetadataGroupImplT<Interface> {
	typedef ContainerMetadataImplT<Interface> 		Me;
	typedef MetadataGroupImplT<Interface> 			Base;
public:

	ContainerMetadataImplT(StringRef name, const MetadataList &content);

	virtual Int Hash() {
		return hash_;
	}

	PageMetadata* GetPageMetadata(Int hashCode);
	ModelMetadata* GetModelMetadata(Int hashCode);

	static void Init() {
	    AbstractType *type = new AbstractType(TypeNameFactory<Interface>::name(), MetadataGroup::MyType());
	    type_ = type;

	    type->AddOperation(new BasicOperation<Int (Me::*)()>(type, "Hash", &Me::Hash));
	    type->AddOperation(new BasicOperation<Typed* (Me::*)(Int), PageMetadata* (Me::*)(Int)>(type, PageMetadataImpl::MyType(), "GetPageMetadata", "hashCode", &Me::GetPageMetadata));
	    type->AddOperation(new BasicOperation<Typed* (Me::*)(Int), PageMetadata* (Me::*)(Int)>(type, PageMetadataImpl::MyType(), "GetPageMetadata", "hash", &Me::GetPageMetadata));
	    type->AddOperation(new BasicOperation<Typed* (Me::*)(Int), ModelMetadata* (Me::*)(Int)>(type, ModelMetadataImpl::MyType(), "GetModelMetadata", "hash", &Me::GetModelMetadata));
	}

    CUBEFS_DECLARE_REFLECTION;

private:
    Int                 hash_;
    PageMetadataMap     page_map_;
    ModelMetadataMap    model_map_;
};


template <typename Interface>
Type* ContainerMetadataImplT<Interface>::type_ 					= NULL;

typedef ContainerMetadataImplT<ContainerMetadata> 					ContainerMetadataImpl;



template <typename Interface>
ContainerMetadataImplT<Interface>::ContainerMetadataImplT(StringRef name, const MetadataList &content) : Base(name, content), hash_(0) {
    Base::set_type() = Metadata::MODEL;

    for (Int c = 0; c < content.size(); c++)
    {
        if (content[c]->GetTypeCode() == Metadata::MODEL)
        {
            ModelMetadata *model = static_cast<ModelMetadata*> (content[c]);
            hash_ = hash_ + model->Hash();

            model_map_[model->Hash()] = model;


            for (Int d = 0; d < model->Size(); d++)
            {
                PageMetadata *page = static_cast<PageMetadata*> (model->GetItem(d));
                page_map_[page->Hash()] = page;
            }
        }
        else {
            //exception;
        }
    }
}

template <typename Interface>
PageMetadata* ContainerMetadataImplT<Interface>::GetPageMetadata(Int hashCode) {
    PageMetadataMap::iterator i = page_map_.find(hashCode);
    if (i != page_map_.end()) {
        return i->second;
    }
    else {
        throw CubefsException(CUBEFS_SOURCE, "Unknown page type hash code");
    }
}

template <typename Interface>
ModelMetadata* ContainerMetadataImplT<Interface>::GetModelMetadata(Int hashCode) {
    ModelMetadataMap::iterator i = model_map_.find(hashCode);
    if (i != model_map_.end()) {
        return i->second;
    }
    else {
        throw CubefsException(CUBEFS_SOURCE, "Unknown model hash code");
    }
}



}}

#undef CUBEFS_SOURCE
#endif
